package com.gwtjs.dao.impl;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import com.gwtjs.dao.GenericDao;
import com.gwtjs.model.Pager;
import com.gwtjs.model.QueryCondition;

/**
 * 缺少直接执行ＳＱＬ的方法
 * @author aGuang
 *
 * @param <T>
 * @param <PK>
 */
@Repository(value = "entityDao")
public class GenericDaoImpl<T, PK extends Serializable> implements
		GenericDao<T, PK> {
	
	protected final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
	
	@Resource(name = "sessionFactory")
	private SessionFactory sessionFactory;

	/**
	 * @return 在session工厂获取当前session,这是hibernate4开始要求的做法
	 */
	public Session getSession() {
		// 事务必须(Required)是开启的,否则获取不到
		return sessionFactory.getCurrentSession();
	}

	// 强制立即更新缓冲数据到数据库（否则仅在事务提交时才更新）
	public void flush() {
		getSession().flush();
	}

	// 强制立即清除缓冲（否则仅在事务提交时才更新）
	public void clear() {
		getSession().clear();
	}

	/**
	 * 提交　　commit
	 * 
	 * @throws HibernateException
	 * @throws SQLException
	 */
	public void commit() throws HibernateException, SQLException {
		// getSession().getTransaction().commit();
	}

	// 加锁指定的实体
	@SuppressWarnings("deprecation")
	public void lock(T entity, LockMode lockMode) {
		getSession().lock(entity, lockMode);
	}

	/* ------------------------------------------------------------------- */

	/**
	 * 取实体最大ID值
	 */
	public Integer getMaxObjectId(final Class<T> entity) {
		StringBuilder sql = new StringBuilder();
		sql.append("SELECT MAX(id) FROM ");
		sql.append(entity.getSimpleName());
		System.out.println(sql.toString());
		Object o = getSession().createQuery(sql.toString()).uniqueResult();
		if (null != o)
			return Integer.parseInt(o.toString());
		return 0;
	}

	/**
	 * 新增或修改
	 * 
	 * @param e
	 */
	public void saveOrUpdate(T e) {
		getSession().saveOrUpdate(e);
	}

	public void save(T e) {
		getSession().save(e); // 这里需要测一下需要不需要返回E
	}

	public void update(T e) {
		getSession().update(e);
	}

	/**
	 * 批量保存
	 * 
	 * @param list
	 */
	public void saveBulk(List<T> list) {
		int i = 1000;
		for (T e : list) {
			i++;
			this.save(e);
			if (i % 20 == 0) {
				// 将本批数据插入数据库，并释放内存
				this.flush();
				this.clear();
			}
			if (i % 1000 == 0) {
				try {
					this.commit();
				} catch (HibernateException e1) {
					e1.printStackTrace();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
		}

	}

	/**
	 * 批量更新
	 * 
	 * @param list
	 */
	public void updateBulk(List<T> list) {
		int i = 1000;
		for (T e : list) {
			i++;
			this.update(e);
			if (i % 20 == 0) {
				// 将本批数据插入数据库，并释放内存
				this.flush();
				this.clear();
			}
			if (i % 1000 == 0) {
				try {
					this.commit();
				} catch (HibernateException e1) {
					e1.printStackTrace();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
		}

	}

	/**
	 * 删除多条 记录
	 * 
	 * @param entityClass
	 * @param att
	 * @param val
	 */
	public void attrDeletes(Class<T> entityClass, Object att, Object... val) {
		StringBuilder sb = new StringBuilder();
		sb.append("DELETE FROM ");
		sb.append(entityClass.getSimpleName());
		sb.append(" WHERE ");
		sb.append(att.toString());
		sb.append(" in (:val)");
		// String hql = "DELETE FROM Article WHERE aid in (:ids)";
		Query q = this.getSession().createQuery(sb.toString());
		sb.delete(0, sb.length());
		q.setParameterList("val", val).executeUpdate();
	}

	/**
	 * 批量删除 ? 待续
	 * 
	 * @param list
	 */
	public void deleteBulk(List<T> list) {
		int i = 1000;
		for (T e : list) {
			i++;
			this.deleteObject(e);
			if (i % 20 == 0) {
				// 将本批数据插入数据库，并释放内存
				this.flush();
				this.clear();
			}
			if (i % 1000 == 0) {
				try {
					this.commit();
				} catch (HibernateException e1) {
					e1.printStackTrace();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
		}

	}

	public void merge(T e) {
		getSession().merge(e); // > ? 这个应该添加返回
	}

	public void delete(Class<T> entityClass, PK id) {
		getSession().delete(this.get(entityClass, id));
	}

	public void deleteObject(T e) {
		getSession().delete(e);
	}

	/**
	 * @deprecated 推荐使用 {@link deleteBulk}
	 */
	public void deletes(T e) {
		// 删除多个，待续
	}

	@SuppressWarnings("unchecked")
	public T get(Class<T> entityClass, PK id) {
		return (T) getSession().get(entityClass, id);
	}

	// 根据主键获取实体并加锁。如果没有相应的实体，返回 null。
	@SuppressWarnings({ "unchecked" })
	@Deprecated
	public T getWithLock(Class<T> entityClass, PK id, LockMode lock) {
		T e = (T) getSession().get(entityClass, id, lock);
		if (e != null) {
			this.flush(); // 立即刷新，否则锁不会生效。
		}
		return e;
	}

	/**
	 * 根据查询条件返回唯一一条 记录
	 * 或者使用
	 * @return 一个实体
	 */
	@SuppressWarnings("unchecked")
	public T get(final Class<T> entityClass, final String propertyName,
			final Object value) {
		Criteria criteria = getSession().createCriteria(entityClass).add(
				Restrictions.eq(propertyName, value));
		T t = (T) criteria.uniqueResult();
		return t;
	}

	/**
	 * 某标识的对象是否存在
	 * 
	 * @param id
	 * @return
	 */
	public boolean exists(Class<T> entityClass, PK id) {
		return get(entityClass, id) != null;
	}

	/**
	 * 按标识修改一个属性的值
	 * 
	 * @param attr
	 * @param val
	 * @param entity
	 * @param id
	 */
	public void updateAttr(Class<T> entityClass, String attr, Object val, PK id) {
		StringBuilder hql = new StringBuilder();
		hql.append("UPDATE ");
		hql.append(entityClass.getSimpleName());
		hql.append(" SET ");
		hql.append(attr);
		hql.append("=:");
		hql.append(attr);
		hql.append(" WHERE ");
		hql.append("id=:id");
		Query query = getSession().createQuery(hql.toString());
		query.setParameter(attr, val);
		query.setParameter("id", id);
		query.executeUpdate();
	}

	/**
	 * <h2>返回总记录数</h2>
	 * <p>
	 * <h3 style="color:#F00">注意：</h3>
	 * 请在排序和分页前提交sql语句,并且WHERE需要大写
	 * </p>
	 * 
	 * @deprecated 此方法暂时没有其它的方法调用
	 * @param sql
	 * @return
	 */
	public int getEntityCount(String sql) {
		return 0;
	}

	/**
     * <h2>根据属性值查询单个对象</h2>
     * 
     * @param entityClass
     *            实体类名
     * @param propertyName
     *            属性名
     * @param value
     *            属性值
     * @return 实体对象
     */
    @SuppressWarnings("unchecked")
	public T findUniqueByProperty(final Class<T> entityClass,
            final String propertyName, final Object value) {
        Criteria criteria = getSession().createCriteria(entityClass).add(
                        Restrictions.eq(propertyName, value));
		T t = (T) criteria.uniqueResult();
		return t;
    }

    /**
     * <h2>根据属性值查询实体是否存在</h2>
     * 
     * @param entityClass
     *            实体类名
     * @param propertyName
     *            参照的属性名
     * @param value
     *            属性值
     * @return 存在则返回true,不存在则返回false
     */
    public boolean isPropertyExist(final Class<T> entityClass,
            final String propertyName, final Object value) {
        Criteria criteria = getSession().createCriteria(entityClass).add(Restrictions.eq(propertyName, value));

		return !criteria.list().isEmpty();
    }

	/*
	 * ------------------------------------ 列表查询
	 * --------------------------------------
	 */

	public Pager<T> findAll(Class<T> entityClass) {
		return this.findAll(entityClass, null, false, 0, 0);
	}

	public Pager<T> findAll(Class<T> entityClass, String ascProperty,
			boolean isAsc, int firstResult, int maxResult) {

		return findAll(entityClass, ascProperty, isAsc, firstResult, maxResult,
				null);
	}

	@SuppressWarnings("unchecked")
	public Pager<T> findAll(Class<T> entityClass, String ascProperty,
			boolean isAsc, int firstResult, int maxResult,
			List<QueryCondition> entities) {

		Criteria crit = getSession().createCriteria(entityClass);

		if (null != entities) {
			for (QueryCondition qc : entities) {
				String property = qc.getExcludeProperty();
				String type = qc.getExcludeType();
				String val = qc.getValue().toString();
				// 需要扩充表达式>\<\=\>=\<=
				if (type == null || "".equals(type)) {
					
					// 需要扩充表达式>\<\=\>=\<=
					if ("start".equals(type)) {
						crit.add(Restrictions.like(property, val,
								MatchMode.START));
					} else if ("end".equals(type)) {
						crit.add(Restrictions.like(property, val,
								MatchMode.END));
					} else if ("exact".equals(type)) {
						crit.add(Restrictions.like(property, val,
								MatchMode.EXACT));
					} else if ("any".equals(type)) {
						crit.add(Restrictions.like(property, val,
								MatchMode.ANYWHERE));
					} else {
						crit.add(Restrictions.eq(property, val));
					}
				} else {
					crit.add(Restrictions.eq(property, val));
				}
			}
		}

		if (isAsc)
			crit.addOrder(Order.desc(ascProperty));

		//crit.createAlias(associationPath, alias)
		//crit.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		crit.setFirstResult(firstResult);
		crit.setMaxResults(maxResult);

		Pager<T> p = new Pager<T>();
		// 这里要监控
		p.setEntityList(crit.list());
		Object size = crit.setProjection(Projections.count("id"))
				.uniqueResult();
		p.setTotalCounts(size.hashCode());

		return p;
	}

	/*
	 * --------------------------------\ 模糊查询 /--------------------------------/
	 */
	/**
	 * 这里需要重新做一下，查询的属性应该是可变数组，值应该是一个
	 */
	@SuppressWarnings("unchecked")
	public Pager<T> findLikeAll(Class<T> entityClass, String ascProperty,
			boolean isAsc, int firstResult, int maxResult,
			List<QueryCondition> entities) {

		Criteria crit = getSession().createCriteria(entityClass);

		if (null != entities) {
			for (QueryCondition qc : entities) {
				String property = qc.getExcludeProperty();
				String type = qc.getExcludeType();
				String val = qc.getValue().toString();
				// 需要扩充表达式>\<\=\>=\<=
				if (type == null || !"".equals(type)) {
					if ("start".equals(type)) {
						crit.add(Restrictions.like(property, val,
								MatchMode.START));
					} else if ("end".equals(type)) {
						crit.add(Restrictions
								.like(property, val, MatchMode.END));
					} else if ("exact".equals(type)) {
						crit.add(Restrictions.like(property, val,
								MatchMode.EXACT));
					} else {
						crit.add(Restrictions.like(property, val,
								MatchMode.ANYWHERE));
					}
				}
			}
		}

		if (!isAsc)
			crit.addOrder(Order.desc(ascProperty));

		crit.setFirstResult(firstResult);
		crit.setMaxResults(maxResult);

		Pager<T> p = new Pager<T>();

		p.setEntityList(crit.list());
		Object size = crit.setProjection(Projections.count("id"))
				.uniqueResult();
		p.setTotalCounts(size.hashCode());

		return p;
	}

	/*
	 * --------------------------------\ 命名及存储过程查询
	 * /--------------------------------/
	 */

	/**
	 * 命名查询的执行
	 */
	/**
	 * 存储过程的执行
	 */
	@SuppressWarnings({ "unchecked" })
	public Pager<T> namedQuery(Class<T> entityClass, String queryName,
			Object val) {
		Query q = getSession().getNamedQuery("proc_getList");
		q.setParameter("content", val);
		Pager<T> p = new Pager<T>();
		p.setEntityList(q.list());
		return p;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

}
